<!-- eslint-disable @typescript-eslint/no-explicit-any -->
<template>
  <el-form
    v-if="model"
    ref="formRef"
    :model="model"
    :rules="rules"
    v-bind="$attrs"
    :validate-on-rule-change="false"
  >
    <template v-for="col in options" :key="col.prop">
      <!-- Single element component -->
      <el-form-item
        v-if="!col.children?.length"
        :label="col.label"
        :prop="col.prop"
      >
        <component
          v-if="col.type !== 'upload'"
          v-model="model[col.prop]"
          :placeholder="col.placeholder"
          v-bind="col.attrs"
          :is="`el-${col.type}`"
        />

        <el-upload
          v-else
          v-bind="col.uploadAttrs"
          :on-preview="onPreview"
          :on-remove="onRemove"
          :on-success="onSuccess"
          :on-error="onError"
          :on-progress="onProgress"
          :on-change="onChange"
          :on-exceed="onExceed"
          :before-upload="beforeUpload"
          :before-remove="beforeRemove"
          :http-request="httpRequest"
        >
          <slot name="uploadArea" />
          <slot name="uploadTip" />
        </el-upload>
      </el-form-item>

      <!-- Multi-element component -->
      <el-form-item
        v-if="col.children?.length"
        :label="col.label"
        :prop="col.prop"
      >
        <component
          v-model="model[col.prop]"
          :placeholder="col.placeholder"
          v-bind="col.attrs"
          :is="`el-${col.type}`"
        >
          <component
            v-for="child in col.children"
            :key="child.value"
            :label="child.label"
            :value="child.value"
            :is="`el-${child.type}`"
          />
        </component>
      </el-form-item>
    </template>

    <el-form-item>
      <slot name="action" :form-ref="formRef" :model="model" />
    </el-form-item>
  </el-form>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue'
import cloneDeep from 'lodash/cloneDeep'
import type { PropType } from 'vue'
import type { FormInstance, UploadFile, UploadFiles } from 'element-plus'
import type { FormOptions, FormEmits } from './types/type'

defineOptions({
  name: 'CustomForm',
})

const props = defineProps({
  options: {
    type: Array as PropType<FormOptions>,
    required: true,
    default: () => []
  },
  httpRequest: {
    type: Function as PropType<(options: any) => Promise<any>>
  }
})

const emit = defineEmits<FormEmits>()

// Refs
const formRef = ref<FormInstance | null>(null)
const model = ref<Record<string, any>>({})
const rules = ref<Record<string, any>>({})

// Computed
const uploadItem = computed(() => 
  props.options.find(item => item.type === 'upload')
)

// Methods
const initForm = () => {
  const initialModel: Record<string, any> = {}
  const initialRules: Record<string, any> = {}

  props.options.forEach(item => {
    if (item.prop) {
      initialModel[item.prop] = item.value
      if (item.rules) {
        initialRules[item.prop] = item.rules
      }
    }
  })

  model.value = cloneDeep(initialModel)
  rules.value = cloneDeep(initialRules)
}

// Upload handlers
const onPreview = (file: UploadFile) => {
  emit('on-preview', file)
}

const onRemove = (file: UploadFile, fileList: UploadFiles) => {
  emit('on-remove', file, fileList)
}

const onSuccess = (response: any, file: UploadFile, fileList: UploadFiles) => {
  if (uploadItem.value?.prop) {
    model.value[uploadItem.value.prop] = { response, file, fileList }
  }
  emit('on-success', { response, file, fileList })
}

const onError = (error: Error, file: UploadFile, fileList: UploadFiles) => {
  emit('on-error', error, file, fileList)
}
const onProgress = (event: any, file: File, fileList: FileList) => {}
const onChange = (file: UploadFile, fileList: UploadFiles) => {
  emit('on-change', file, fileList)
}
const onExceed = (file: File, fileList: FileList) => {}
const beforeUpload = (file: File) => {}
const beforeRemove = (file: File, fileList: FileList) => {}
// Lifecycle
onMounted(initForm)

// Watchers
watch(
  () => props.options,
  () => {
    initForm()
  },
  { deep: true }
)
</script>
